import os
import re
import time
from paramiko.client import SSHClient
import paramiko
import logging
import requests

log_format = '%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
logging.basicConfig(level=logging.INFO, format=log_format)


# 此文件需要和Ali_cloud_SLB放在相同目录下
# 此文件应当存放在跳板机上
#
class DeployMain:
    # remote_ip远程部署的IP，不是跳板机IP user登陆的用户名root|www|* ssh_key本地私钥路径
    def __init__(self,
                 remote_ip: str,
                 env: str,
                 project_name: str,
                 package_name: str,
                 env_json: dict) -> None:

        self.Remote_IP = remote_ip
        self.Env = env
        self.Project_Name = project_name
        self.Package_Name = package_name

        self.Project_json = env_json["Project"][env][project_name]
        self.Sys_json = env_json["Deploy_Config"]
        self.User = env_json["Deploy_Config"]['USER']
        self.Port = env_json["Deploy_Config"]['Port']
        self.SSH_Key = env_json["Deploy_Config"]['SSH_Key']

    # 创建需要远程部署机器的SSH长链接
    @staticmethod
    def create_client(
            remote_ip: str,
            user: str,
            ssh_key: str,
            port: int) -> SSHClient:
        private_key = paramiko.RSAKey.from_private_key_file(ssh_key)
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname=remote_ip, port=port, username=user, pkey=private_key)
        return ssh

    # 在跳板机器上进行的本地操作，sync等
    @staticmethod
    def local_command_action(command: str) -> os:
        result_out = os.system(command)
        logging.warning(f'执行结果: {result_out}')
        return result_out

    # 首先需要调用create_client创建ssh长链接
    # command是远程部署机器上的操作包括：备份二进制文件、解压压缩包、删除压缩包、重启服务
    def ssh_command_action(self, command: str) -> create_client:
        # 调用create_client返回ssh长链接
        ssh = DeployMain.create_client(self.Remote_IP, self.User, self.SSH_Key, self.Port)
        stdin, stdout, stderr = ssh.exec_command(command)
        result_out = stdout.read().decode()
        logging.warning(f'执行结果: {result_out}')
        # 每次执行完command以后会断掉链接
        ssh.close()
        return result_out

    # 备份需要备份的二进制文件
    def backup(self) -> ssh_command_action:
        for service_file in self.Project_json["Backup"]:
            backup_command = f'cd  {self.Sys_json["Deploy_Root_Path"]}/{self.Project_Name} && ' \
                             f'mv {service_file} {service_file}.bak'
            self.ssh_command_action(backup_command)

    # 同步操作，从本地机器rsync打包好的package同步到远程部署机器上，远程部署机器IP由Ali_cloud_SLB获取
    # 因为需要上下流量操作，所以每次只能操作一台机器
    def sync_file(self) -> ssh_command_action:
        build_dir = f'{self.Sys_json["Script_Work_Path"]}/{self.Project_Name}'
        # 创建远程部署服务器的部署目录
        mkdir_dir = f'mkdir -p {self.Sys_json["Deploy_Root_Path"]}/{self.Project_Name}'
        self.ssh_command_action(mkdir_dir)

        # 同步跳板机的package到远程部署服务器上的部署目录里
        rsync_command = f'rsync -avP {build_dir}/{self.Package_Name} ' \
                        f'{self.Remote_IP}:{self.Sys_json["Deploy_Root_Path"]}/{self.Project_Name}/{self.Package_Name}'
        self.local_command_action(rsync_command)

        # 判断Package_Name后缀是否为tar.gz或tar
        if re.findall(r'(.*).tar.gz$|(.*).tar$', self.Package_Name, re.S):

            # 部分项目为传压缩包到跳板机，所以需要登陆到远程部署机器上进行：备份二进制文件和配置文件、解压缩、删除压缩包的操作
            tar_command = f'cd {self.Sys_json["Deploy_Root_Path"]}/{self.Project_Name} &&  ' \
                          f'tar xf {self.Package_Name} && ' \
                          f'rm -f {self.Package_Name} && ' \
                          f'{self.Project_json["After_Tar"]}'
            self.ssh_command_action(tar_command)

    def sync_config_file(self):
        pass

    def restart_service(self) -> ssh_command_action:
        restart_service_command = self.Project_json["Restart"]
        self.ssh_command_action(restart_service_command)

        # 重启完以后进行健康检查，如果健康检查接口不通将会停止更新
        time.sleep(3)
        return self.check_service_health()

    def check_service_health(self) -> restart_service:
        if 'Check_Health' in self.Project_json:
            check_health = requests.get(url=f'http://{self.Remote_IP}:'
                                            f'{self.Project_json["Check_Health"]["Port"]}'
                                            f'{self.Project_json["Check_Health"]["URL"]}')
            logging.info(f'{check_health.text}')
            if check_health.status_code == requests.codes.ok:
                return True
            else:
                return False
